/*
 * Package il.ac.biu.cs.grossmm.impl.keys
 * File KeyPatternImpl.java
 * Created on Jul 20, 2006
 *
 */
package il.ac.biu.cs.grossmm.api.keys;

/**
 * Implementation of <tt>KeyPattern</tt> interface based on a resizable array.
 */
public class ArrayKeyPattern extends BaseKeyPattern {

	private PatternEntry[] entries;

	private int hashCode;

	private int size = 0;

	/**
	 * Creates a new simple value-based array key pattern
	 * 
	 * @param isMask
	 *            whether the new pattern should be a mask pattern
	 * @param value
	 *            the base-value of the pattern
	 */
	public ArrayKeyPattern(boolean isMask, Object value)
	{
		this(isMask, value, true);
	}

	/**
	 * Creates a new simple type-based array key pattern
	 * 
	 * @param isMask
	 *            whether the new pattern should be a mask pattern
	 * @param clazz
	 *            the base-type of the pattern
	 */
	public ArrayKeyPattern(boolean isMask, Class clazz)
	{
		this(isMask, clazz, false);
	}

	/**
	 * Creates a new simple array key pattern
	 * 
	 * @param isMask
	 *            whether the new pattern should be a mask pattern
	 * @param base
	 *            value or type
	 * @param asValue
	 *            if true, the base argiment is value, otherwise it is
	 *            considered class
	 */
	public ArrayKeyPattern(boolean isMask, Object base, boolean asValue)
	{
		super(isMask, base, asValue);

		this.entries = new PatternEntry[2];

		recalcHash();
	}

	/**
	 * Creates a key pattern
	 * 
	 * @param isMask
	 *            whether the new pattern should be a mask pattern
	 * @param base
	 *            value or type
	 * @param asValue
	 *            if true, the base argiment is value, otherwise it is
	 *            considered class
	 * @param entries
	 *            entries for this pattern
	 */
	public ArrayKeyPattern(boolean isMask, Object base, boolean asValue,
		PatternEntry... entries)
	{
		super(isMask, base, asValue);

		this.entries = entries;
		this.size = entries.length;

		recalcHash();
	}

	/**
	 * Creates a new key pattern based on existing pattern, with a given mask
	 * and with specified pattern entry (replaces corresponding entry in the
	 * given pattern, if any)
	 * 
	 * @param pattern
	 *            the base pattern
	 * @param entry
	 *            the entry to add
	 */
	public ArrayKeyPattern(KeyPattern pattern, PatternEntry entry)
	{
		super(pattern.isMask(), pattern.valueClass() == null ? pattern.value()
			: pattern.valueClass(), pattern.valueClass() == null);
		
		this.entries = new PatternEntry[pattern.size() + 1];

		int len = pattern.size();
		int sz = 0;

		boolean seen = false;

		for (int i = 0; i < len; i++) {
			PatternEntry pe = pattern.entry(i);
			if (!seen && entry.getAttribute().equals(pe.getAttribute())) {
				seen = true;
				this.entries[sz++] = entry;
			} else
				this.entries[sz++] = pe;
		}

		if (!seen) {
			this.entries[sz++] = entry;
		}

		this.size = sz;

		recalcHash();
	}

	private void recalcHash()
	{
		hashCode = 0;
		if (value != null)
			hashCode += value.hashCode();
		if (valueClass != null)
			hashCode += valueClass.hashCode();
		if (isMask)
			hashCode = Integer.MAX_VALUE - hashCode;

		for (int i = 0; i < size; i++) {
			PatternEntry e = entries[i];
			hashCode += e.getAttribute().hashCode();
			hashCode += e.getPattern().hashCode();
		}
	}

	public void add(PatternEntry patternEntry)
	{
		if (entries.length == size) {
			PatternEntry[] newEntries = new PatternEntry[size * 2];
			System.arraycopy(entries, 0, newEntries, 0, size);
			entries = newEntries;
		}

		entries[size++] = patternEntry;

		recalcHash();
	}

	public void add(Attribute attribute, KeyPattern pattern, boolean isMandatory)
	{
		add(new PatternEntry(attribute, pattern, isMandatory));
	}

	public void add(Attribute attribute, Object value, boolean isMandatory)
	{
		KeyPattern pattern = new ArrayKeyPattern(false, value, true);
		add(attribute, pattern, isMandatory);
	}

	public void add(Attribute attribute, Class value, boolean isMandatory)
	{
		KeyPattern pattern = new ArrayKeyPattern(false, value, false);
		add(attribute, pattern, isMandatory);
	}

	@Override
	public PatternEntry entry(int i)
	{
		return entries[i];
	}

	@Override
	public KeyPattern subpattern(Attribute a)
	{
		for (PatternEntry ke : entries)
			if (ke.getAttribute().equals(a))
				return ke.getPattern();

		return null;
	}

	@Override
	public int size()
	{
		return size;
	}

	@Override
	public int hashCode()
	{
		return hashCode;
	}

	public PatternEntry entry(Attribute a)
	{
		for (int i = 0; i < size; i++) {
			PatternEntry ke = entries[i];
			if (ke.getAttribute().equals(a))
				return ke;
		}

		return null;
	}
}
